package br.com.persist.core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import br.com.persist.api.Criteria;
import br.com.persist.api.Entity;
import br.com.persist.api.PersistentContext;
import br.com.persist.api.RuntimeExecutionException;

public class InMemoryPersistentContext implements PersistentContext {

	private List<Object> ctx = new ArrayList<Object>();
	private Map<String, List<Object>> index = new HashMap<String, List<Object>>();

	public Object persist(Object object) throws RuntimeExecutionException {
		if (object == null) {
			throw new RuntimeExecutionException("Cannot persist a null reference.");
		}

		int index = ctx.indexOf(object);
		if (index == -1) {
			ctx.add(object);
			index(object);
		}
		return object;
	}

	protected void index(Object object) {
		Set<String> entities = getAllEntitiesFrom(object);

		for (String entity : entities) {
			if (!index.containsKey(entity)) {
				index.put(entity, new ArrayList<Object>());
			}
			index.get(entity).add(object);
		}
	}

	protected Set<String> getAllEntitiesFrom(Object object) {
		Class<?> klass = object.getClass();

		Set<String> entities = new HashSet<String>();

		getAllEntities(klass, entities);

		return entities;
	}

	protected void getAllEntities(Class<?> klass, Set<String> entities) {
		entities.add(klass.getName());

		Class<?> superClass = klass.getSuperclass();
		if (superClass != null) {
			getAllEntities(superClass, entities);
		}

		for (Class<?> interfaceClass : klass.getInterfaces()) {
			getAllEntities(interfaceClass, entities);
		}
	}

	@SuppressWarnings("unchecked")
	public List<Object> doSearch(Criteria criteria) throws RuntimeExecutionException {
		Entity entity = criteria.getEntity();

		if (ctx.isEmpty()) {
			return Collections.EMPTY_LIST;
		} else {
			List<Object> result = index.get(entity.getFullName());

			if (!criteria.getConditions().isEmpty()) {
				throw new UnsupportedOperationException();
			}

			return result;
		}
	}

	public void delete(Object object) {
		ctx.remove(object);
	}

	public Criteria search() {
		return new Criteria();
	}

	public Criteria search(Class<?> entity) {
		return new Criteria().entity(entity);
	}
}
